package com.yuchs.test.log4j;

import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.ManagerFactory;
import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager;
import org.apache.logging.log4j.core.appender.db.nosql.NoSQLAppender;
import org.apache.logging.log4j.core.appender.db.nosql.NoSQLProvider;
import org.apache.logging.log4j.core.appender.db.nosql.mongo.MongoDBConnection;
import org.apache.logging.log4j.core.appender.db.nosql.mongo.MongoDBObject;

public class PlayerLogDatabaseManager extends AbstractDatabaseManager {
	
	private NoSQLProvider<?> mProvider;
	private MongoDBConnection mConnection;
	
	private PlayerLogDatabaseManager(final String name, final int bufferSize,
			final NoSQLProvider<?> provider){
		
		super(name, bufferSize);
		mProvider = provider;
    }

	@Override
	protected void connectInternal() {
		try {
			mConnection = (MongoDBConnection)mProvider.getConnection();
		} catch (final Exception e) {
		    LOGGER.error("Failed to obtain a connection to the NoSQL database in manager [{}].", this.getName(), e);
		}
	}

	@Override
	protected void disconnectInternal() {
		try {
            if (mConnection != null && !mConnection.isClosed()) {
            	mConnection.close();
            }
        } catch (final Exception e) {
            LOGGER.warn("Error while closing NoSQL database connection in manager [{}].", this.getName(), e);
        }
	}

	@Override
	protected void writeInternal(LogEvent event) {
		final MongoDBObject entity = mConnection.createObject();
		entity.set("lev", event.getLevel());
		entity.set("msg",event.getMessage() == null ? null : event.getMessage().getFormattedMessage());
		entity.set("t",(int)(event.getMillis() / 1000));

		Object[] parms = event.getMessage().getParameters();
		if(parms != null && parms.length >= 1){
			entity.set("uid",parms[0].toString());
		}
		
		mConnection.insertObject(entity);
	}
	
	private static final NoSQLDatabaseManagerFactory FACTORY = new NoSQLDatabaseManagerFactory();
	
    /**
     * Creates a NoSQL manager for use within the {@link NoSQLAppender}, or returns a suitable one if it already exists.
     *
     * @param name The name of the manager, which should include connection details and hashed passwords where possible.
     * @param bufferSize The size of the log event buffer.
     * @param provider A provider instance which will be used to obtain connections to the chosen NoSQL database.
     * @return a new or existing NoSQL manager as applicable.
     */
    public static PlayerLogDatabaseManager getNoSQLDatabaseManager(final String name, final int bufferSize,
                                                                  final NoSQLProvider<?> provider) {
        return AbstractDatabaseManager.getManager(name, new FactoryData(bufferSize,provider), FACTORY);
    }
    
    /**
     * Encapsulates data that {@link NoSQLDatabaseManagerFactory} uses to create managers.
     */
    private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData {
        private final NoSQLProvider<?> provider;

        protected FactoryData(final int bufferSize, final NoSQLProvider<?> provider) {
            super(bufferSize);
            this.provider = provider;
        }
    }
    
    /**
     * Creates managers.
     */
    private static final class NoSQLDatabaseManagerFactory implements
            ManagerFactory<PlayerLogDatabaseManager, FactoryData> {
        @Override
        public PlayerLogDatabaseManager createManager(final String name, final FactoryData data) {
            return new PlayerLogDatabaseManager(name, data.getBufferSize(), data.provider);
        }
    }
}
